package com.maimao.info.service;

import cn.hutool.core.util.ObjectUtil;
import com.baidu.fsg.uid.impl.CachedUidGenerator;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.maimao.core.constant.Constants;
import com.maimao.core.constant.ListData;
import com.maimao.core.exception.MaiMaoDaoException;
import com.maimao.core.model.resp.PageResult;
import com.maimao.core.model.resp.Result;
import com.maimao.core.utils.DateUtils;
import com.maimao.core.utils.MapUtils;
import com.maimao.info.client.ShopClient;
import com.maimao.info.mapper.CouponUserMapper;
import com.maimao.mg.repository.MgCouponMapper;
import com.maimao.model.info.CouponUser;
import com.maimao.model.info.MgCoupon;
import com.maimao.model.info.input.ConfirmCoupon;
import com.maimao.model.info.input.UseCouponInput;
import com.maimao.model.info.vo.CouponSearch;
import com.maimao.model.order.confirm.CouponSimple;
import com.maimao.model.user.Shop;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author MaoLin Wang
 * @date 2021/1/5 4:27 下午
 */
@Service
public class CouponService {
    @Autowired
    private ShopClient shopClient;


    @Autowired
    private MgCouponMapper mgCouponMapper;

    @Autowired
    private CouponUserMapper couponUserMapper;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private CachedUidGenerator cachedUidGenerator;

    @Autowired
    private CouponService couponService;

    public PageResult<MgCoupon> webShopCouponData(String userId, Integer page, Integer size, CouponSearch couponSearch) {
        PageHelper.startPage(page, size);
        Result<Shop> shopRes = shopClient.findShopByOwner(Long.valueOf(userId), 0);
        if (shopRes == null || shopRes.getData() == null) {
            return new PageResult<MgCoupon>(ListData.EMPTY_LIST, 0L);
        }
        Shop shop = shopRes.getData();
        Query query = new Query();
        Criteria criteria = Criteria.where("shopId").is(shop.getId());
        buildSearchCriteria(couponSearch, criteria);
        query.addCriteria(criteria);
        List<MgCoupon> mgCoupons = mongoTemplate.find(query, MgCoupon.class);
        if (mgCoupons != null && mgCoupons.size() > 0) {
            for (MgCoupon coupon : mgCoupons) {
                coupon.setStock(coupon.getCount() - coupon.getTakeCount());
            }
        }

        return new PageResult<>(mgCoupons);

    }

    private void buildSearchCriteria(CouponSearch couponSearch, Criteria criteria) {
        if (couponSearch.getStatus() != null && !couponSearch.getStatus().equals(-1)) {
            criteria.and("deleted").is(couponSearch.getStatus());
        }
        if (couponSearch.getUseType() != null && !couponSearch.getUseType().equals(-1)) {
            criteria.and("useType").is(couponSearch.getUseType());
        }
        if (couponSearch.getWhat() != null && !couponSearch.getWhat().equals(-1)) {
            criteria.and("what").is(couponSearch.getWhat());
        }
        if (couponSearch.getWhere() != null && !couponSearch.getWhere().equals(-1)) {
            criteria.and("where").is(couponSearch.getWhere());
        }
    }

    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void addCoupon(MgCoupon coupon) {
//        couponMapper.insert(coupon);
        coupon.setId(cachedUidGenerator.getUID());
        mgCouponMapper.save(coupon);
    }

    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void updateCoupon(MgCoupon coupon) {
        mgCouponMapper.save(coupon);
    }

    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public boolean updateDeleted(Long id, Integer deleted) {
        if (deleted.equals(Constants.STATUS_0)) {
            //恢复
            MgCoupon mgCoupon = mongoTemplate.findById(id, MgCoupon.class);
            if (mgCoupon == null || mgCoupon.getEndTime().compareTo(DateUtils.now()) < 0) {
                return false;
            }
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(id));
        Update update = new Update();
        update.set("deleted", deleted);
        mongoTemplate.updateFirst(query, update, MgCoupon.class);
        return true;
    }

    /**
     * 当前用户是否领取对应的优惠券
     *
     * @param couponIds 要检查的优惠券id
     * @param userId    用户id
     * @return Map<优惠券id, 该用户是否领取过>
     */
    public Map<Long, Boolean> couponUserExist(List<Long> couponIds, Long userId) {
        List<CouponUser> couponUsers = couponUserMapper.selectByUserIdAndCouponIds(userId, couponIds);
        HashMap<Long, Boolean> res = Maps.newHashMapWithExpectedSize(MapUtils.initCapacity(couponIds.size()));
        if (couponUsers != null && couponUsers.size() > 0) {
            List<Long> couponIdList = couponUsers.stream().map(CouponUser::getCouponId).collect(Collectors.toList());
            for (Long couponId : couponIds) {
                if (couponIdList.contains(couponId)) {
                    res.put(couponId, true);
                } else {
                    res.put(couponId, false);
                }
            }
        }
        return res;
    }

    public List<MgCoupon> queryByShopId(Long shopId, Long userId) {
        Query query = new Query();
        query.addCriteria(buildShopTimeCriteria(shopId));
        List<MgCoupon> coupons = mongoTemplate.find(query, MgCoupon.class);
        if (userId != null) {
            List<Long> couponIds = coupons.stream().map(MgCoupon::getId).collect(Collectors.toList());
            Map<Long, Boolean> checkMap = this.couponUserExist(couponIds, userId);
            if (checkMap != null && checkMap.size() > 0) {
                for (MgCoupon coupon : coupons) {
                    Boolean taked = checkMap.get(coupon.getId());
                    coupon.setCan(taked == null ? true : !taked);
                    int stock = coupon.getCount() - coupon.getTakeCount();
                    coupon.setStock(stock);
                    coupon.setCan(stock > 0 ? coupon.getCan() : false);
                }
            }
        }
        return coupons;


    }

    private Criteria buildShopTimeCriteria(Long shopId) {
        return Criteria.where("shopId").is(shopId).and("endTime").gte(DateUtils.now()).and("startTime").lte(DateUtils.now());
    }

    private Criteria buildSysTimeCriteria() {
        return Criteria.where("where").in(4, 5).and("endTime").gte(DateUtils.now()).and("startTime").lte(DateUtils.now());
    }

    /**
     * 领取优惠券
     *
     * @param couponId
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public Result take(Long couponId, Long userId) {
        //前端控制了 但还是要检查是否领取过
        List<CouponUser> couponUsers = couponUserMapper.selectByUserIdAndCouponIds(userId, Lists.newArrayList(couponId));
        if (couponUsers == null || couponUsers.size() <= 0) {
            Query query = new Query();
            query.addCriteria(Criteria.where("_id").is(couponId)).addCriteria(new Criteria() {
                @Override
                public Document getCriteriaObject() {
                    Document document = new Document();
                    document.put("$where", "this.takeCount<this.count");
                    return document;
                }
            });
            Update update = new Update();
            update.inc("takeCount", 1);
            UpdateResult updateResult = mongoTemplate.updateFirst(query, update, couponCollection);
            if (updateResult.getMatchedCount() == 1) {
                Optional<MgCoupon> targetCoupon = mgCouponMapper.findMgCouponById(couponId);
                if (targetCoupon.isPresent()) {
                    MgCoupon mgCoupon = targetCoupon.get();
                    CouponUser couponUser = CouponUser.builder().couponId(couponId)
                            .status(1)
                            .used(false)
                            .userId(userId).build();
                    if (mgCoupon.getUseType().equals(2)){
                        //限时优惠券结束时间为领取时间+使用期限（天数）
                        couponUser.setStartTime(DateUtils.now());
                        couponUser.setEndTime(DateUtils.now().plusDays(mgCoupon.getLimit()));
                    }else {
                        couponUser.setStartTime(mgCoupon.getStartTime());
                        couponUser.setEndTime(mgCoupon.getEndTime());
                    }
                    couponUserMapper.insert(couponUser);
                    return Result.ok("领取成功");

                }


            } else {
                return Result.fail("优惠券不存在或全部领取完毕");
            }

        }

        return Result.fail("您已经领取过，不可重复领取");


    }

    /**
     * 领取并返回当前用户对当前店铺的可用优惠券
     */
    public List<CouponSimple> takeAndQueryShopUsefulShopCoupon(ConfirmCoupon confirmCoupon) {
        //有效期内的 + 当前店铺的 或 当前商品列表可用的 + 立减的 或 confirmCoupon.limit 达到门槛的
        Query query = new Query();
        query.addCriteria(buildShopTimeCriteria(confirmCoupon.getShopId()));
        List<MgCoupon> shopCoupons = mongoTemplate.find(query, MgCoupon.class);
        List<CouponSimple> shopCouponSimpleList = getCouponSimples(confirmCoupon, shopCoupons);

        return shopCouponSimpleList == null ? ListData.EMPTY_LIST : shopCouponSimpleList;
    }

    /**
     * 领取并返回当前用户的所以系统可用优惠券
     */
    public List<CouponSimple> takeAndQuerySysUsefulShopCoupon(ConfirmCoupon confirmCoupon) {
        List<MgCoupon> sysCoupons = mongoTemplate.find(new Query().addCriteria(buildSysTimeCriteria()), MgCoupon.class);
        List<CouponSimple> sysCouponSimples = getCouponSimples(confirmCoupon, sysCoupons);
        return sysCouponSimples == null ? ListData.EMPTY_LIST : sysCouponSimples;

    }

    private List<CouponSimple> getCouponSimples(ConfirmCoupon confirmCoupon, List<MgCoupon> shopCoupons) {
        if (shopCoupons != null && shopCoupons.size() > 0) {
            List<MgCoupon> usefulCoupons = shopCoupons.stream().filter(mgCoupon -> {
                if (mgCoupon.getWhat().equals(1) && mgCoupon.getMin().compareTo(confirmCoupon.getLimit()) > 0) {
                    //满减
                    return false;
                } else {
                    //立减或满减达到门槛
                    if (mgCoupon.getWhere().equals(1) || mgCoupon.getWhere().equals(2)) {
                        //指定商品
                        List<String> goodsId = mgCoupon.getGoodsId();
                        List<Long> skuIds = confirmCoupon.getGoodsId();
                        List<String> skuStr = new ArrayList<>(skuIds.size());
                        skuIds.stream().forEach(id -> skuStr.add(String.valueOf(id)));
                        skuStr.retainAll(goodsId);
                        return skuStr.size() > 0;
                    } else if (mgCoupon.getWhere().equals(4)) {
                        //专区优惠 TODO 暂时未引入专区
                        return false;
                    } else {
                        //店铺优惠或全场优惠直接返回
                        return true;
                    }
                }

            }).collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(usefulCoupons)) {
                List<Long> couponIds = usefulCoupons.stream().map(MgCoupon::getId).collect(Collectors.toList());
                //去掉使用过的优惠券（可能提交订单后尚未）
                List<CouponUser> couponUsers = couponUserMapper.selectAllByUserIdAndCouponIds(confirmCoupon.getUserId(), couponIds);
                if (couponUsers != null && couponUsers.size() > 0) {
                    List<Long> invalidCouponIds = new ArrayList<>();

                    for (CouponUser couponUser : couponUsers) {
                        //去掉尚未开始的、过期的和已经使用过的优惠券
                        if (couponUser.getStartTime().compareTo(DateUtils.now()) > 0 || couponUser.getEndTime().compareTo(DateUtils.now()) < 0 || couponUser.getUsed()) {
                            invalidCouponIds.add(couponUser.getCouponId());
                        }
                    }
                    if (invalidCouponIds.size() > 0) {
                        usefulCoupons = usefulCoupons.stream().filter(mgCoupon -> !invalidCouponIds.contains(mgCoupon.getId())).collect(Collectors.toList());
                    }
                }

                //返回可用优惠券
                ArrayList<CouponSimple> couponSimpleList = Lists.newArrayListWithCapacity(usefulCoupons.size());
                CouponSimple couponSimple = null;
                CouponSimple max = null;
                for (MgCoupon usefulCoupon : usefulCoupons) {
                    //领取未领取的可用优惠券
                    couponService.take(usefulCoupon.getId(), confirmCoupon.getUserId());
                    couponSimple = new CouponSimple();
                    couponSimple.setCouponId(usefulCoupon.getId());
                    couponSimple.setDiscount(usefulCoupon.getDiscountPrice());
                    couponSimple.setWhat(usefulCoupon.getWhat());
                    couponSimple.setMin(usefulCoupon.getMin());
                    couponSimpleList.add(couponSimple);
                    if (max == null) {
                        max = couponSimple;
                    } else if (couponSimple.getDiscount().compareTo(max.getDiscount()) > 0) {
                        max = couponSimple;
                    }
                }
                for (CouponSimple simple : couponSimpleList) {
                    if (simple.getCouponId().equals(max.getCouponId())) {
                        simple.setMax(true);
                        break;
                    }
                }
                return couponSimpleList;

            }
        }
        return ListData.EMPTY_LIST;
    }

    @Value("${maimao.mongo.collection.couponCollection}")
    private String couponCollection;


    /**
     * 修改用户优惠券使用状态
     *
     * @param useCouponInput
     */
    @Transactional(rollbackFor = MaiMaoDaoException.class)
    public void useCoupon(UseCouponInput useCouponInput) {
        couponUserMapper.updateUsed(useCouponInput.getUserId(), useCouponInput.getOrderId(), useCouponInput.getUsedTime(), useCouponInput.getCouponIds(), useCouponInput.getUsed());
    }
}
